﻿using Microsoft.EntityFrameworkCore;
using Reader.Chromely.Core.Configuration;
using Reader.Chromely.Core.Logging;
using Reader.Core.Application.Models;
using Reader.Core.DataAccessObject;
using Reader.Core.DataAccessObject.Models;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Reader.Core.Application
{
    public class Books
    {
        private readonly ReaderConfiguration _config;
        private readonly string _basePath;
        private const string _baseBookPath = "books";
        private readonly Applications app;

        public Books(IChromelyConfiguration config)
        {
            _config = config as ReaderConfiguration;
            _basePath = _config.WebRootPath;
            app = new Applications(config);
            if (!Directory.Exists(Path.Combine(_basePath, _baseBookPath)))
            {
                Directory.CreateDirectory(Path.Combine(_basePath, _baseBookPath));
            }
        }

        private static bool HasDownload(string path)
        {
            var download = false;
            if (Directory.Exists(path))
            {
                if(Directory.GetFiles(path,"*.*", SearchOption.AllDirectories).Length > 0)
                {
                    download = true;
                }
            }
            return download;
        }
        private readonly Func<string, bool> Func1 = HasDownload;
        public async Task<Result> AllBooksAsync()
        {
            using var db = new ApplicationDBContext();
            var books = await db.Books
                .Include(b => b.Author)
                .Include(b => b.Site)
                .Include(b => b.BookShelf)
                .OrderByDescending(b => b.CreateDateTime)
                .Select(b => new AllBookModel
                {
                    Id = b.Id,
                    Name = b.Name,
                    SiteId = b.Site.Id,
                    SiteName = b.Site.Name,
                    SiteUrl = b.Url,
                    ShelfId = b.BookShelf.Id,
                    ShelfName = b.BookShelf.Name,
                    AuthorId = b.Author.Id,
                    AuthorName = b.Author.Name,
                    Url = b.ReadPosition.IsNullOrWhiteSpace() ? b.LocalPath : b.ReadPosition,
                    Completed = b.Completed,
                    Cover = b.Cover,
                    Intro = b.Intro,
                    Download = Func1($"{_basePath}{b.LocalPath}")
                }).ToListAsync();
            return ResultStatus<List<AllBookModel>>.Success(books);
        }
        public async Task<Result> NewBookAsync(NewBookModel newBook)
        {
            if (!newBook.ShelfId.HasValue)
            {
                newBook.ShelfId = 1;
            }
            if (!newBook.Completed.HasValue)
            {
                newBook.Completed = false;
            }
            string path = Path.Combine(_baseBookPath, Guid.NewGuid().ToString()).Replace("\\", "/");
            if (path.PadLeft(1) != "/")
            {
                path = "/" + path;
            }
            newBook.Path = path;
            var author = await app.Authors.GetAuthorAsync(newBook.Author);
            using var db = new ApplicationDBContext();
            var book = await db.Books.SingleOrDefaultAsync(b => b.Name == newBook.Name && b.SiteId == newBook.Site.Id);
            string localPath = localPath = $"{_basePath}{path.Replace("/", @"\")}";
            if(book != null)
            {
                localPath = $"{_basePath}{book.LocalPath.Replace("/", @"\")}";
                newBook.Path = book.LocalPath;
            }
            if (!Directory.Exists(localPath))
            {
                Directory.CreateDirectory(localPath);
            }
            if (!newBook.Cover.IsNullOrWhiteSpace())
            {
                var filename = Path.GetFileName(newBook.Cover);
                if (!File.Exists(Path.Combine(localPath, filename)))
                {
                    var result = await app.Net.DownloadStreamToFileAsync(newBook.Cover, localPath);
                    if (result.Success)
                    {
                        newBook.Cover = Path.Combine(newBook.Path, filename).Replace("\\", "/");
                    }
                }
            }
            if (book == null)
            {
                book = new Book
                {
                    Name = newBook.Name,
                    Cover = newBook.Cover,
                    Intro = newBook.Intro,
                    SiteId = newBook.Site.Id,
                    Completed = newBook.Completed.HasValue ? newBook.Completed.Value : false,
                    AuthorId = author.Id,
                    ShelfId = newBook.ShelfId.Value,
                    Url = newBook.Site.Url,
                    LocalPath = newBook.Path,
                    CreateDateTime = DateTime.Now
                };
                await db.Books.AddAsync(book);
            }
            
            try
            {
                await CreateIndexAsync(newBook);
                await db.SaveChangesAsync();
                return ResultStatus.Success();
            }
            catch (Exception ex)
            {
                Logger.Instance.Log.Error(ex);
                return ResultStatus.Fail(ex.Message);
            }
        }
        private async Task CreateIndexAsync(NewBookModel book)
        {
            var indexTemplate = await Utility.GetEmbedStringSourceAsync("index.html");
            indexTemplate = indexTemplate
                .Replace("{Name}", book.Name)
                .Replace("{Site}", book.Site.Name)
                .Replace("{Cover}", book.Cover)
                .Replace("{Intro}", book.Intro)
                .Replace("{Completed}", book.Completed.Value ? "已完本" : "连载中");
            var panel = Regex.Match(indexTemplate, @"\s{4}<div class=""panel panel-default center-block tile"">([.\s\S]*?)\n\s{4}</div>", RegexOptions.IgnoreCase).Value;
            var index = 1;
            var panelTemp = string.Empty;
            foreach (var v in book.Volumes)
            {
                var li = (new Regex("(<li>[^\n]*)")).Match(indexTemplate).Value;
                var liTemp = string.Empty;
                foreach (var chapter in book.Chapters.Where(c => c.VolumeId == v.Id))
                {
                    liTemp += li
                        .Replace("{chapterId}", index.ToString())
                        .Replace("{chapter}", chapter.Name)
                        .Replace("{chapterUrl}", chapter.Url);
                    index++;
                }
                panelTemp += panel.Replace("{Volume}", v.Name).Replace(li, liTemp);
            }
            indexTemplate = indexTemplate.Replace(panel, panelTemp);
            var indexWritePath = $"{_basePath}{Path.Combine(book.Path, "index.html")}";
            File.WriteAllText(indexWritePath, indexTemplate, Encoding.UTF8);
        }
        public async Task CreateChapterAsync(string url,string content)
        {
            var chapterTemplate = await Utility.GetEmbedStringSourceAsync("chapter.html");
        }
        public async Task NewBookChaptersAsync(NewBookModel book, string path)
        {
            var indexTemplate = await Utility.GetEmbedStringSourceAsync("index.html");
            var chapterTemplate = await Utility.GetEmbedStringSourceAsync("chapter.html");
            indexTemplate = indexTemplate
                .Replace("{Name}", book.Name)
                .Replace("{Site}", book.Site.Name)
                .Replace("{Cover}", book.Cover)
                .Replace("{Intro}", book.Intro)
                .Replace("{Completed}", book.Completed.Value ? "已完本" : "连载中");
            var panel = Regex.Match(indexTemplate, @"\s{4}<div class=""panel panel-default center-block tile"">([.\s\S]*?)\n\s{4}</div>", RegexOptions.IgnoreCase).Value;
            var index = 1;
            var panelTemp = "";
            foreach (var v in book.Volumes)
            {
                var li = (new Regex("(<li>[^\n]*)")).Match(indexTemplate).Value;
                var liTemp = "";
                foreach (var chapter in book.Chapters.Where(c => c.VolumeId == v.Id))
                {
                    liTemp += li
                        .Replace("{chapterId}", index.ToString())
                        .Replace("{chapter}", chapter.Name)
                        .Replace("{chapterUrl}", chapter.Url);
                    int prev = index - 1, next = index + 1;
                    if (index == 1)
                    {
                        prev = 1;
                    }
                    if (index == book.Chapters.Count)
                    {
                        next = index;
                    }
                    var content = "";
                    if (!chapter.Content.IsNullOrWhiteSpace())
                    {
                        var lines = chapter.Content.Split(new char[] { '\n' });
                        foreach (var line in lines)
                        {
                            content += $"<p>{line.Trim()}</p>";
                        }
                    }
                    chapterTemplate = chapterTemplate
                        .Replace("{Name}", chapter.Name)
                        .Replace("{Prev}", prev.ToString())
                        .Replace("{Next}", next.ToString())
                        .Replace("{Site}", book.Site.Name)
                        .Replace("{chapterUrl}", chapter.Url)
                        .Replace("{Progress}", $"{index * 100.00m / book.Chapters.Count:0.00}%")
                        .Replace("{Context}", content);
                    var chapterWritePath = Path.Combine(Path.Combine(_basePath, path), $"{index}.html");
                    File.WriteAllText(chapterWritePath, chapterTemplate, Encoding.UTF8);
                    index++;
                }
                panelTemp += panel.Replace("{Volume}", v.Name).Replace(li, liTemp);
            }
            indexTemplate = indexTemplate.Replace(panel, panelTemp);
            var indexWritePath = Path.Combine(Path.Combine(_basePath, path), "index.html");
            File.WriteAllText(indexWritePath, indexTemplate, Encoding.UTF8);
        }
        public async Task<List<BookInfoSource>> GetBookInfoSourceAsync()
        {
            using var db = new ApplicationDBContext();
            var sources = await db.BookInfoSources.ToListAsync();
            return sources;
        }
        public async Task<Result> RemoveBook(int bookId)
        {
            using var db = new ApplicationDBContext();
            var book = await db.Books.SingleOrDefaultAsync(b => b.Id == bookId);
            if(book != null)
            {
                var path = $"{_basePath}{book.LocalPath.Replace("/", @"\")}";
                if (Directory.Exists(path))
                {
                    Directory.Delete(path, true);
                }
                db.Books.Remove(book);
                await db.SaveChangesAsync();
                return ResultStatus.Success();
            }
            else
            {
                return ResultStatus.Fail("找不到小说");
            }
        }
    }
}
